package com.ruoyi.coin.utils.okx;

import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONUtil;
import com.ruoyi.coin.enums.BuyEnum;
import com.ruoyi.coin.enums.OklinkChainEnum;
import com.ruoyi.coin.enums.OklinkTranFlagEnum;
import com.ruoyi.coin.model.okx.OkxApproveModel;
import com.ruoyi.coin.model.okx.OkxPriceModel;
import com.ruoyi.coin.model.okx.OkxSwapModel;
import com.ruoyi.coin.utils.CoinUtils;
import com.ruoyi.coin.utils.oklink.OklinkUtils;
import com.ruoyi.coin.utils.web3j.Web3jUtils;
import com.ruoyi.common.config.CoinConfig;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.blade.Func;
import com.ruoyi.common.utils.blade.tool.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.web3j.crypto.Credentials;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Date;
import java.util.List;

@Slf4j
public class OkxUtil {

    private static final String DOMAIN="https://www.okx.com";


    private static Credentials credentials = Credentials.create(CoinConfig.getWallet_pri_key());//密钥


    public static boolean getBnb2TokenPrice(OklinkChainEnum oklinkChainEnum,BuyEnum buyEnum ,String tokenAddress,Integer decimal){
        StringBuffer sb=new StringBuffer();
        sb.append("chainId=").append(oklinkChainEnum.getChainId()).append("&");

        if(Func.equals(buyEnum,BuyEnum.BUY)){//购买
            BigDecimal buyAmount = oklinkChainEnum.getBuyAmount();
            String amount = buyAmount.multiply(BigDecimal.TEN.pow(18)).stripTrailingZeros().toPlainString();

            sb.append("amount=").append(amount).append("&");
            sb.append("fromTokenAddress=").append(oklinkChainEnum.getContract()).append("&");
            sb.append("toTokenAddress=").append(tokenAddress);

        }else if(Func.equals(buyEnum,BuyEnum.SELL)){//卖出
            BigDecimal sellAmount = OklinkUtils.getTokenAmount(oklinkChainEnum, CoinConfig.getWallet_address(), tokenAddress);
            String tokenAmount = sellAmount.multiply(BigDecimal.TEN.pow(decimal)).multiply(BigDecimal.valueOf(0.998)).stripTrailingZeros().toPlainString();//怕余额问题，卖剩一点点 千分2

            sb.append("amount=").append(tokenAmount).append("&");
            sb.append("fromTokenAddress=").append(tokenAddress).append("&");
            sb.append("toTokenAddress=").append(oklinkChainEnum.getContract());
        }
        String API="/api/v5/dex/aggregator/quote?"+sb.toString();
        //获取时间
        String timestamp = getTimestamp();
        String sign = generateOkxSignGet(API, timestamp);//签名

        //发送请求
        String body = sendHttp(API,timestamp,sign);
        OkxPriceModel okxPriceModel = JSONUtil.toBean(body, OkxPriceModel.class);
        if(Func.isEmpty(okxPriceModel)){
            return false;
        }
        String code = okxPriceModel.getCode();
        if(!Func.equals(code,"0")){
            return false;
        }
        List<OkxPriceModel.DataModel> dataList = okxPriceModel.getData();
        if(Func.isEmpty(dataList)){
            return false;
        }
        OkxPriceModel.DataModel dataModel = dataList.get(0);
        if(Func.isEmpty(dataModel)){
            return false;
        }
        List<OkxPriceModel.RouterModel> dexRouterList = dataModel.getDexRouterList();
        if(Func.isEmpty(dexRouterList)){
            return false;
        }
        return true;
    }


    //授权TOKEN
    public static boolean approveToken(OklinkChainEnum oklinkChainEnum,String tokenAddress){
        //是否有授权了
        BigDecimal approveAmount = OklinkUtils.getToken20ApproveAmount(oklinkChainEnum, CoinConfig.getWallet_address(), tokenAddress);
        if(approveAmount.compareTo(BigDecimal.valueOf(-1))==0){
            return true;
        }
        if(approveAmount.compareTo(BigDecimal.ZERO)==1){
            return true;
        }
        //授权 金额
        approveAmount = new BigDecimal("100000000000000000000000000000000000000000");//超级大金额

        log.info("授权金额："+approveAmount);

        StringBuffer sb=new StringBuffer();
        sb.append("chainId=").append(oklinkChainEnum.getChainId()).append("&");
        sb.append("tokenContractAddress=").append(tokenAddress).append("&");
        sb.append("approveAmount=").append(approveAmount);

        String API="/api/v5/dex/aggregator/approve-transaction?"+sb.toString();
        //获取时间
        String timestamp = getTimestamp();

        String sign = generateOkxSignGet(API,timestamp);
        //发送请求
        String body = sendHttp(API,timestamp,sign);
        OkxApproveModel resultModel = JSONUtil.toBean(body,OkxApproveModel.class);
        String code = resultModel.getCode();
        if(!Func.equals(code,"0")){
            throw new ServiceException("授权失败");
        }
        List<OkxApproveModel.DataModel> dataList = resultModel.getData();
        if(Func.isEmpty(dataList)){
            throw new ServiceException("数据为空1");
        }
        OkxApproveModel.DataModel dataModel = dataList.get(0);
        if(Func.isEmpty(dataModel)){
            throw new ServiceException("数据为空2");
        }


        //获取nonce
        BigInteger nonce = Web3jUtils.getNonce(oklinkChainEnum, CoinConfig.getWallet_address());

        //获取签名
        BigInteger gasPrice = new BigInteger(dataModel.getGasPrice());
        BigInteger gas = new BigInteger(dataModel.getGasLimit());
        String data = dataModel.getData();


        String chainSign = CoinUtils.getChainSign(oklinkChainEnum,credentials, nonce, tokenAddress, BigInteger.ZERO,gasPrice,gas,data);

        //上链
        String hash = Web3jUtils.transOnline(oklinkChainEnum, chainSign);
        if(Func.isEmpty(hash)){
            throw new ServiceException("哈希为空");
        }

        boolean tranFlag=getTranFlag(oklinkChainEnum,hash);
        System.out.println("授权状态："+tranFlag);
        return tranFlag;
    }


    public static String swap(OklinkChainEnum oklinkChainEnum,BuyEnum buyEnum,String tokenAddress,Integer decimal){
        StringBuffer sb=new StringBuffer();
        sb.append("chainId=").append(oklinkChainEnum.getChainId()).append("&");

        if(Func.equals(buyEnum,BuyEnum.BUY)){//BNB ETH BASE->token
            BigDecimal buyAmount = oklinkChainEnum.getBuyAmount();
            String amount = buyAmount.multiply(BigDecimal.TEN.pow(18)).stripTrailingZeros().toPlainString();
            sb.append("amount=").append(amount).append("&");
            sb.append("fromTokenAddress=").append(oklinkChainEnum.getContract()).append("&");
            sb.append("toTokenAddress=").append(tokenAddress).append("&");
        }
        //卖出
        else if(Func.equals(buyEnum,BuyEnum.SELL)){//token -> bnb
            BigDecimal sellAmount = OklinkUtils.getTokenAmount(oklinkChainEnum, CoinConfig.getWallet_address(), tokenAddress);
            String tokenAmount = sellAmount.multiply(BigDecimal.TEN.pow(decimal)).multiply(BigDecimal.valueOf(0.998)).stripTrailingZeros().toPlainString();//怕余额问题，卖剩一点点 千分2
            sb.append("amount=").append(tokenAmount).append("&");
            sb.append("fromTokenAddress=").append(tokenAddress).append("&");
            sb.append("toTokenAddress=").append(oklinkChainEnum.getContract()).append("&");
        }

        //滑点
        sb.append("slippage=").append(oklinkChainEnum.getSlippage()).append("&");
        sb.append("userWalletAddress=").append(CoinConfig.getWallet_address());
        String API="/api/v5/dex/aggregator/swap?"+sb.toString();
        //获取时间
        String timestamp = getTimestamp();

        String sign = generateOkxSignGet(API,timestamp);
        //发送请求
        String body = sendHttp(API,timestamp,sign);
        OkxSwapModel resultModel = JSONUtil.toBean(body,OkxSwapModel.class);
        System.out.println("body======="+body);
        String code = resultModel.getCode();
        if(!Func.equals(code,"0")){
            return null;
        }
        OkxSwapModel.DataModel dataModel = resultModel.getData().get(0);
        if(Func.isEmpty(dataModel)){
            return null;
        }
        OkxSwapModel.TxModel txModel = dataModel.getTx();
        if(Func.isEmpty(txModel)){
            return null;
        }
        //获取nonce
        BigInteger nonce = Web3jUtils.getNonce(oklinkChainEnum, CoinConfig.getWallet_address());

        //获取签名
        BigInteger gasPrice = new BigInteger(txModel.getGasPrice());
        BigInteger gas = new BigInteger(txModel.getGas());
        BigInteger value = new BigInteger(txModel.getValue());
        String data = txModel.getData();
        String to = txModel.getTo();


        String chainSign = CoinUtils.getChainSign(oklinkChainEnum,credentials, nonce,to ,value, gasPrice,gas,data);
        //上链
        String hash = Web3jUtils.transOnline(oklinkChainEnum, chainSign);

        if(Func.isEmpty(hash)){
            throw new ServiceException("哈希为空");
        }
        System.out.println("hash===="+hash);
        return hash;
    }

    private static String getTimestamp(){
        Date now = DateUtil.now();
        OffsetDateTime offsetDateTime = now.toInstant().atOffset(ZoneOffset.UTC); // 使用UTC时区

        DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
        String timestamp = offsetDateTime.format(formatter);
        return timestamp;
    }

    /**
     * 授权okx请求授权
     * @param api
     * @return
     */
    private static String generateOkxSignGet(String api,String timestamp) {

        List<String> paramsList=new ArrayList<>();
        paramsList.add(timestamp);
        paramsList.add("GET");
        paramsList.add(api);
        // 拼接参数
        StringBuilder paramStr = new StringBuilder();
        for (String key : paramsList) {
            paramStr.append(key);
        }
        // 生成签名
        return Base64.getEncoder().encodeToString(hmacSha256(paramStr.toString().getBytes(StandardCharsets.UTF_8), CoinConfig.getOkx_secret_key().getBytes(StandardCharsets.UTF_8)));
    }
    private static byte[] hmacSha256(byte[] data, byte[] key) {
        try {
            String HMAC_SHA256 = "HmacSHA256";
            Mac mac = Mac.getInstance(HMAC_SHA256);
            mac.init(new SecretKeySpec(key, HMAC_SHA256));
            return mac.doFinal(data);
        } catch (Exception e) {
            throw new RuntimeException("Failed to generate HMAC SHA256", e);
        }
    }


    /**
     * 发送http请求
     * @param api
     * @param timestamp
     * @param sign
     * @return
     */
    private static String sendHttp(String api,String timestamp,String sign){
        for (int i = 0; i < 3; i++) {
            try{
                String body = HttpRequest.get(DOMAIN+api)
                        .setHttpProxy(CoinConfig.getProxy_ip(), CoinConfig.getProxy_port())
                        .header("OK-ACCESS-PROJECT", CoinConfig.getOkx_project_id() )
                        .header("OK-ACCESS-KEY", CoinConfig.getOkx_api_key())
                        .header("OK-ACCESS-PASSPHRASE", CoinConfig.getOkx_passphrase())
                        .header("OK-ACCESS-TIMESTAMP",timestamp)
                        .header("OK-ACCESS-SIGN",sign )
                        .execute().body();
                return body;
            }catch (Exception e){
                try{
                    Thread.sleep(1000L);
                }catch (Exception e1){}

            }
        }
        return null;
    }


    //获取交易状态
    private static boolean getTranFlag(OklinkChainEnum oklinkChainEnum,String hash){
        boolean tranFlag=false;
        for (int i = 0; i < 10; i++) {
            try{
                Thread.sleep(5000L);
                OklinkTranFlagEnum tranFlagEnum = OklinkUtils.getTranFlagByHash(oklinkChainEnum, hash);
                if(Func.equals(OklinkTranFlagEnum.SUCCESS,tranFlagEnum)){
                    tranFlag=true;
                    break;
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }


        return tranFlag;
    }



}
